const std = @import("std");

tag: Tag,
loc: Loc,

pub const Loc = struct {
    start: u32,
    end: u32,

    const Extra = struct {
        line: u32,
        col: u32,
        line_start: u32,
        line_end: u32,
    };

    pub fn slice(self: Loc, source: []const u8) []const u8 {
        return source[self.start..self.end];
    }

    pub fn extraInfo(self: Loc, source: []const u8) Extra {
        var result = Extra{
            .line = 1,
            .col = 1,
            .line_start = 0,
            .line_end = @intCast(source.len),
        };

        for (source[0..self.start], 0..) |c, i| {
            if (c == '\n') {
                result.line += 1;
                result.line_start = @as(u32, @intCast(i)) + 1;
            }
        }

        for (source[self.end..], 0..) |c, i| {
            if (c == '\n') {
                result.line_end = self.end + @as(u32, @intCast(i));
                break;
            }
        }

        result.col += self.start - result.line_start;
        return result;
    }
};

pub const Tag = enum {
    eof,
    invalid,

    ident,
    number,

    paren_left,
    paren_right,
    brace_left,
    brace_right,
    bracket_left,
    bracket_right,

    dot,
    comma,
    colon,
    semicolon,
    arrow,
    attr,

    equal,
    equal_equal,
    bang,
    bang_equal,
    ampersand,
    ampersand_equal,
    ampersand_ampersand,
    pipe,
    pipe_equal,
    pipe_pipe,
    tilde,
    plus,
    plus_equal,
    plus_plus,
    minus,
    minus_equal,
    minus_minus,
    asterisk,
    asterisk_equal,
    slash,
    slash_equal,
    percent,
    percent_equal,
    xor,
    xor_equal,
    angle_bracket_left,
    angle_bracket_left_equal,
    angle_bracket_angle_bracket_left,
    angle_bracket_angle_bracket_left_equal,
    angle_bracket_right,
    angle_bracket_right_equal,
    angle_bracket_angle_bracket_right,
    angle_bracket_angle_bracket_right_equal,
    underscore,

    k_enable,
    k_requires,

    k_fn,
    k_var,
    k_let,
    k_const,
    k_override,
    k_type,

    k_if,
    k_else,
    k_loop,
    k_while,
    k_for,
    k_break,
    k_continue,
    k_continuing,
    k_discard,
    k_switch,
    k_case,
    k_default,
    k_return,
    k_const_assert,
    k_bitcast,

    k_bool,
    k_u32,
    k_i32,
    k_f16,
    k_f32,
    k_vec2,
    k_vec3,
    k_vec4,
    k_mat2x2,
    k_mat2x3,
    k_mat2x4,
    k_mat3x2,
    k_mat3x3,
    k_mat3x4,
    k_mat4x2,
    k_mat4x3,
    k_mat4x4,
    k_ptr,
    k_array,
    k_atomic,
    k_struct,
    k_sampler,
    k_sampler_comparison,
    k_texture_depth_2d,
    k_texture_depth_2d_array,
    k_texture_depth_cube,
    k_texture_depth_cube_array,
    k_texture_depth_multisampled_2d,
    k_texture_external,
    k_texture_multisampled_2d,
    k_texture_1d,
    k_texture_2d,
    k_texture_2d_array,
    k_texture_3d,
    k_texture_cube,
    k_texture_cube_array,
    k_texture_storage_1d,
    k_texture_storage_2d,
    k_texture_storage_2d_array,
    k_texture_storage_3d,

    k_false,
    k_true,

    template_left,
    template_right,

    pub fn symbol(self: Tag) []const u8 {
        return switch (self) {
            .eof => "EOF",
            .invalid => "invalid bytes",
            .ident => "an identifier",
            .number => "a number literal",

            .paren_left => "(",
            .paren_right => ")",
            .brace_left => "{",
            .brace_right => "}",
            .bracket_left => "[",
            .bracket_right => "]",
            .dot => ".",
            .comma => ",",
            .colon => ":",
            .semicolon => ";",
            .arrow => "->",
            .attr => "@",
            .equal => "=",
            .equal_equal => "==",
            .bang => "!",
            .bang_equal => "!=",
            .ampersand => "&",
            .ampersand_equal => "&=",
            .ampersand_ampersand => "&&",
            .pipe => "|",
            .pipe_equal => "|=",
            .pipe_pipe => "||",
            .tilde => "~",
            .plus => "+",
            .plus_equal => "+=",
            .plus_plus => "++",
            .minus => "-",
            .minus_equal => "-=",
            .minus_minus => "--",
            .asterisk => "*",
            .asterisk_equal => "*=",
            .slash => "/",
            .slash_equal => "/=",
            .percent => "%",
            .percent_equal => "%=",
            .xor => "^",
            .xor_equal => "^=",
            .angle_bracket_left => "<",
            .angle_bracket_left_equal => "<=",
            .angle_bracket_angle_bracket_left => "<<",
            .angle_bracket_angle_bracket_left_equal => "<<=",
            .angle_bracket_right => ">",
            .angle_bracket_right_equal => ">=",
            .angle_bracket_angle_bracket_right => ">>",
            .angle_bracket_angle_bracket_right_equal => ">>=",
            .underscore => "_",
            .k_enable => "enable",
            .k_requires => "requires",
            .k_fn => "fn",
            .k_var => "var",
            .k_let => "let",
            .k_const => "const",
            .k_override => "override",
            .k_type => "type",
            .k_if => "if",
            .k_else => "else",
            .k_loop => "loop",
            .k_while => "while",
            .k_for => "for",
            .k_break => "break",
            .k_continue => "continue",
            .k_continuing => "continuing",
            .k_discard => "discard",
            .k_switch => "switch",
            .k_case => "case",
            .k_default => "default",
            .k_return => "return",
            .k_const_assert => "const_assert",
            .k_bitcast => "bitcast",
            .k_bool => "bool",
            .k_u32 => "u32",
            .k_i32 => "i32",
            .k_f16 => "f16",
            .k_f32 => "f32",
            .k_vec2 => "vec2",
            .k_vec3 => "vec3",
            .k_vec4 => "vec4",
            .k_mat2x2 => "mat2x2",
            .k_mat2x3 => "mat2x3",
            .k_mat2x4 => "mat2x4",
            .k_mat3x2 => "mat3x2",
            .k_mat3x3 => "mat3x3",
            .k_mat3x4 => "mat3x4",
            .k_mat4x2 => "mat4x2",
            .k_mat4x3 => "mat4x3",
            .k_mat4x4 => "mat4x4",
            .k_ptr => "ptr",
            .k_array => "array",
            .k_atomic => "atomic",
            .k_struct => "struct",
            .k_sampler => "sampler",
            .k_sampler_comparison => "sampler_comparison",
            .k_texture_depth_2d => "texture_depth_2d",
            .k_texture_depth_2d_array => "texture_depth_2d_array",
            .k_texture_depth_cube => "texture_depth_cube",
            .k_texture_depth_cube_array => "texture_depth_cube_array",
            .k_texture_depth_multisampled_2d => "texture_depth_multisampled_2d",
            .k_texture_external => "texture_external",
            .k_texture_multisampled_2d => "texture_multisampled_2d",
            .k_texture_1d => "texture_1d",
            .k_texture_2d => "texture_2d",
            .k_texture_2d_array => "texture_2d_array",
            .k_texture_3d => "texture_3d",
            .k_texture_cube => "texture_cube",
            .k_texture_cube_array => "texture_cube_array",
            .k_texture_storage_1d => "texture_storage_1d",
            .k_texture_storage_2d => "texture_storage_2d",
            .k_texture_storage_2d_array => "texture_storage_2d_array",
            .k_texture_storage_3d => "texture_storage_3d",
            .k_false => "false",
            .k_true => "true",
            .template_left => "<",
            .template_right => ">",
        };
    }
};

pub const keywords = std.StaticStringMap(Tag).initComptime(.{
    .{ "enable", .k_enable },
    .{ "requires", .k_requires },
    .{ "fn", .k_fn },
    .{ "var", .k_var },
    .{ "let", .k_let },
    .{ "const", .k_const },
    .{ "override", .k_override },
    .{ "type", .k_type },
    .{ "if", .k_if },
    .{ "else", .k_else },
    .{ "loop", .k_loop },
    .{ "while", .k_while },
    .{ "for", .k_for },
    .{ "break", .k_break },
    .{ "continue", .k_continue },
    .{ "continuing", .k_continuing },
    .{ "discard", .k_discard },
    .{ "switch", .k_switch },
    .{ "case", .k_case },
    .{ "default", .k_default },
    .{ "return", .k_return },
    .{ "const_assert", .k_const_assert },
    .{ "bitcast", .k_bitcast },
    .{ "bool", .k_bool },
    .{ "u32", .k_u32 },
    .{ "i32", .k_i32 },
    .{ "f16", .k_f16 },
    .{ "f32", .k_f32 },
    .{ "vec2", .k_vec2 },
    .{ "vec3", .k_vec3 },
    .{ "vec4", .k_vec4 },
    .{ "mat2x2", .k_mat2x2 },
    .{ "mat2x3", .k_mat2x3 },
    .{ "mat2x4", .k_mat2x4 },
    .{ "mat3x2", .k_mat3x2 },
    .{ "mat3x3", .k_mat3x3 },
    .{ "mat3x4", .k_mat3x4 },
    .{ "mat4x2", .k_mat4x2 },
    .{ "mat4x3", .k_mat4x3 },
    .{ "mat4x4", .k_mat4x4 },
    .{ "ptr", .k_ptr },
    .{ "array", .k_array },
    .{ "atomic", .k_atomic },
    .{ "struct", .k_struct },
    .{ "sampler", .k_sampler },
    .{ "sampler_comparison", .k_sampler_comparison },
    .{ "texture_depth_2d", .k_texture_depth_2d },
    .{ "texture_depth_2d_array", .k_texture_depth_2d_array },
    .{ "texture_depth_cube", .k_texture_depth_cube },
    .{ "texture_depth_cube_array", .k_texture_depth_cube_array },
    .{ "texture_depth_multisampled_2d", .k_texture_depth_multisampled_2d },
    .{ "texture_external", .k_texture_external },
    .{ "texture_multisampled_2d", .k_texture_multisampled_2d },
    .{ "texture_1d", .k_texture_1d },
    .{ "texture_2d", .k_texture_2d },
    .{ "texture_2d_array", .k_texture_2d_array },
    .{ "texture_3d", .k_texture_3d },
    .{ "texture_cube", .k_texture_cube },
    .{ "texture_cube_array", .k_texture_cube_array },
    .{ "texture_storage_1d", .k_texture_storage_1d },
    .{ "texture_storage_2d", .k_texture_storage_2d },
    .{ "texture_storage_2d_array", .k_texture_storage_2d_array },
    .{ "texture_storage_3d", .k_texture_storage_3d },
    .{ "false", .k_false },
    .{ "true", .k_true },
});

pub const reserved = blk: {
    @setEvalBranchQuota(3000);
    break :blk std.StaticStringMap(void).initComptime(.{
        .{ "NULL", {} },
        .{ "Self", {} },
        .{ "abstract", {} },
        .{ "active", {} },
        .{ "alignas", {} },
        .{ "alignof", {} },
        .{ "as", {} },
        .{ "asm", {} },
        .{ "asm_fragment", {} },
        .{ "async", {} },
        .{ "attribute", {} },
        .{ "auto", {} },
        .{ "await", {} },
        .{ "become", {} },
        .{ "binding_array", {} },
        .{ "cast", {} },
        .{ "catch", {} },
        .{ "class", {} },
        .{ "co_await", {} },
        .{ "co_return", {} },
        .{ "co_yield", {} },
        .{ "coherent", {} },
        .{ "column_major", {} },
        .{ "common", {} },
        .{ "compile", {} },
        .{ "compile_fragment", {} },
        .{ "concept", {} },
        .{ "const_cast", {} },
        .{ "consteval", {} },
        .{ "constexpr", {} },
        .{ "constinit", {} },
        .{ "crate", {} },
        .{ "debugger", {} },
        .{ "decltype", {} },
        .{ "delete", {} },
        .{ "demote", {} },
        .{ "demote_to_helper", {} },
        .{ "do", {} },
        .{ "dynamic_cast", {} },
        .{ "enum", {} },
        .{ "explicit", {} },
        .{ "export", {} },
        .{ "extends", {} },
        .{ "extern", {} },
        .{ "external", {} },
        .{ "fallthrough", {} },
        .{ "filter", {} },
        .{ "final", {} },
        .{ "finally", {} },
        .{ "friend", {} },
        .{ "from", {} },
        .{ "fxgroup", {} },
        .{ "get", {} },
        .{ "goto", {} },
        .{ "groupshared", {} },
        .{ "highp", {} },
        .{ "impl", {} },
        .{ "implements", {} },
        .{ "import", {} },
        .{ "inline", {} },
        .{ "instanceof", {} },
        .{ "interface", {} },
        .{ "layout", {} },
        .{ "lowp", {} },
        .{ "macro", {} },
        .{ "macro_rules", {} },
        .{ "match", {} },
        .{ "mediump", {} },
        .{ "meta", {} },
        .{ "mod", {} },
        .{ "module", {} },
        .{ "move", {} },
        .{ "mut", {} },
        .{ "mutable", {} },
        .{ "namespace", {} },
        .{ "new", {} },
        .{ "nil", {} },
        .{ "noexcept", {} },
        .{ "noinline", {} },
        .{ "nointerpolation", {} },
        .{ "noperspective", {} },
        .{ "null", {} },
        .{ "nullptr", {} },
        .{ "of", {} },
        .{ "operator", {} },
        .{ "package", {} },
        .{ "packoffset", {} },
        .{ "partition", {} },
        .{ "pass", {} },
        .{ "patch", {} },
        .{ "pixelfragment", {} },
        .{ "precise", {} },
        .{ "precision", {} },
        .{ "premerge", {} },
        .{ "priv", {} },
        .{ "protected", {} },
        .{ "pub", {} },
        .{ "public", {} },
        .{ "readonly", {} },
        .{ "ref", {} },
        .{ "regardless", {} },
        .{ "register", {} },
        .{ "reinterpret_cast", {} },
        .{ "require", {} },
        .{ "resource", {} },
        .{ "restrict", {} },
        .{ "self", {} },
        .{ "set", {} },
        .{ "shared", {} },
        .{ "sizeof", {} },
        .{ "smooth", {} },
        .{ "snorm", {} },
        .{ "static", {} },
        .{ "static_assert", {} },
        .{ "static_cast", {} },
        .{ "std", {} },
        .{ "subroutine", {} },
        .{ "super", {} },
        .{ "target", {} },
        .{ "template", {} },
        .{ "this", {} },
        .{ "thread_local", {} },
        .{ "throw", {} },
        .{ "trait", {} },
        .{ "try", {} },
        .{ "type", {} },
        .{ "typedef", {} },
        .{ "typeid", {} },
        .{ "typename", {} },
        .{ "typeof", {} },
        .{ "union", {} },
        .{ "unless", {} },
        .{ "unorm", {} },
        .{ "unsafe", {} },
        .{ "unsized", {} },
        .{ "use", {} },
        .{ "using", {} },
        .{ "varying", {} },
        .{ "virtual", {} },
        .{ "volatile", {} },
        .{ "wgsl", {} },
        .{ "where", {} },
        .{ "with", {} },
        .{ "writeonly", {} },
        .{ "yield", {} },
    });
};
